/* 
 * Copyright (C) 2014 Bartosz Stankiewicz.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */
package pl.poznan.put.et.kstio.invoice_ocr;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonReader;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartInput;

import pl.poznan.put.et.kstio.invoice_ocr.model.InvoiceOcrProfile;

/**
 *
 * @author BartaZ
 */
@Stateless
@Path("test")
public class UploadRestBean {

	private final String tempDir = System.getProperty("jboss.server.temp.dir");
	private final String fileSeparator = System.getProperty("file.separator");
	private static final Logger logger = Logger.getLogger(UploadRestBean.class);

	@EJB
	private TessInstance tess;

	@EJB
	private GlobalProperies gp;

	@EJB
	private InvoiceOcrRepository ior;

	@EJB
	private PdfProfile pdfProfile;

	public UploadRestBean() {
		logger.debug(" constructor tempDir: " + tempDir);
	}

	@PostConstruct
	public void init() {
		logger.error("init tempDir: " + tempDir);
	}

	@GET
	@Produces({ MediaType.APPLICATION_JSON + "; charset=utf-8" })
	@Path("test")
	public Response test() {
		logger.debug("test tempDir: " + tempDir);
		ResponseBuilder rb = Response.ok("{\"polskie\":\"ąęĄĘńżźŃŻŹ\", \"isDbConnected\":" + ior.testConnection() + "}");
//		rb.encoding("utf8");
		return rb.build();
	}

	@POST
	@Path("post")
	@Consumes({ MediaType.MULTIPART_FORM_DATA })
	@Produces({ MediaType.APPLICATION_JSON + "; charset=utf-8" })
	public Response post(MultipartInput input) {
		String status = "ok";
		String message = "";

		try {
			for (InputPart part : input.getParts()) {
				String partName = getPartName(part.getHeaders());
				if ("pdf".equals(partName.toLowerCase())) {
					MediaType mediaType = part.getMediaType();
					String filename = mediaType.getParameters().get("name");
					String mimeType = mediaType.getType() + "/" + mediaType.getSubtype();

					InputStream inputStream = part.getBody(InputStream.class, null);
					File file = writeFile(inputStream, tempDir + fileSeparator + filename);
					logger.debug("post: file location: " + tempDir + fileSeparator + filename);
					logger.debug("post: file AbsolutePath: " + file.getAbsolutePath());
//					inputStream.close();
					String invoiceText = tess.doPdfOCR(file);
					String[] nips = tess.getNipFromPdf(invoiceText);
					for (String nip : nips) {
						logger.debug("post: nip: " + nip);
						message += nip + ',';
					}
					final int msgLen = message.length();
					message = message.substring(0, msgLen > 0 ? msgLen - 1 : msgLen);
					// file.delete();
				}
			}
		} catch (IOException ex) {
			logger.fatal(ex.getMessage());
			status = "error";
			message = ex.getMessage();
		} finally {
			if (input != null) {
				input.close();
			}
		}
		return getResponse(status, message);
	}

	@POST
	@Path("newProfile")
	@Consumes({MediaType.MULTIPART_FORM_DATA})
	@Produces({MediaType.APPLICATION_JSON + "; charset=utf-8"})
	public Response newProfile(MultipartInput input) {
		InputStream inputStream;
		String status = "ok";
		String message = "ok";

		try {
			File file = null;
			// TODO: zamienić na HashMap albo coś co implementuje Serializable
			Map<String, String> metadata = null;
			for (InputPart part : input.getParts()) {
				String partName = getPartName(part.getHeaders());
				if ("pdf".equals(partName.toLowerCase())) {
					MediaType mediaType = part.getMediaType();
					String filename = mediaType.getParameters().get("name");
//					String mimeType = mediaType.getType() + "/" + mediaType.getSubtype();

					inputStream = part.getBody(InputStream.class, null);
					if (logger.isDebugEnabled()) {
						logger.debug("newProfile");
						logger.debug("newProfile: " + pdfProfile);
						logger.debug("newProfile is bean null: " + (pdfProfile == null));
					}
					
					file = writeFile(inputStream, tempDir + fileSeparator + filename);
//					pdfProfile.saveProfile(file);
//					logger.debug("post: file location: " + tempDir + fileSeparator + filename);
//					logger.debug("post: file AbsolutePath: " + file.getAbsolutePath());
//					//inputStream.close();
//					String invoiceText = tess.doPdfOCR(file);
//					List<String> nips = tess.getNipFromPdf(invoiceText);
//					message = nips.toString();
//					//file.delete();
				} else if ("metadata".equals(partName.toLowerCase())) {
					final String jsonString = part.getBodyAsString();
					metadata = parseJson(jsonString);
				}
			}
			pdfProfile.saveProfile(file, metadata);
		} catch (IOException ex) {
			logger.fatal(ex.getMessage());
			status = "error";
			message = ex.getMessage();
		} finally {
			if (input != null)
				input.close();
		}
		return getResponse(status, message);
	}
	
	@POST
	@Path("ocrByProfille")
	@Consumes({ MediaType.MULTIPART_FORM_DATA })
	@Produces({ MediaType.APPLICATION_JSON + "; charset=utf-8" })
	public Response ocrByProfille(MultipartInput input) {
		String status = "ok";
		String message = "";
		File file = null;
		Map<String, String> metadata = null;

		try {
			for (InputPart part : input.getParts()) {
				String partName = getPartName(part.getHeaders());
				if ("pdf".equals(partName.toLowerCase())) {
					MediaType mediaType = part.getMediaType();
					String filename = mediaType.getParameters().get("name");
					InputStream inputStream = part.getBody(InputStream.class, null);
					file = writeFile(inputStream, tempDir + fileSeparator + filename);
				} else if ("metadata".equals(partName.toLowerCase())) {
					final String jsonString = part.getBodyAsString();
					metadata = parseJson(jsonString);
				}
			}
		} catch (IOException ex) {
			logger.fatal(ex.getMessage());
			status = "error";
			message = ex.getMessage();
		} finally {
			if (input != null) {
				input.close();
			}
		}
		if (!"error".equals(status) && file != null && metadata != null && metadata.containsKey("profileName")) {
			List<InvoiceOcrProfile> propertiesList = ior.getMaxRevProfile(metadata.get("profileName"));
			Map<String, Object> results = tess.getInvoiceProperty(file, propertiesList);
			JsonObjectBuilder job = Json.createObjectBuilder( );
			for(String key:results.keySet()) {
				job.add(key, results.get(key).toString());
			}
			message = job.build().toString();
		}
		return getResponse(status, message);
	}

	
	@GET
	@Path("getProfile")
	@Produces({MediaType.APPLICATION_JSON + "; charset=utf-8"})
	public Response newProfile(@QueryParam("profile") String profile) {
		List<InvoiceOcrProfile> profileProperies = ior.getMaxRevProfile(profile);
		return getResponse("OK", "OK");
	}

	private String getPartName(MultivaluedMap<String, String> headers) {
		final String searchKey = " name=\"";
		String cd = headers.get("Content-Disposition").get(0);
		int idxStart = cd.indexOf(searchKey);
		idxStart += searchKey.length();
		int idxStop = cd.indexOf("\"", idxStart);
		return cd.substring(idxStart, idxStop);
	}

	private File writeFile(InputStream contentStream, String filename) throws IOException {
		byte[] content = IOUtils.toByteArray(contentStream);
		File file = new File(filename);
		if (!file.exists()) {
			file.createNewFile();
		}
		try (FileOutputStream fop = new FileOutputStream(file)) {
			fop.write(content);
			fop.flush();
			fop.close();
		} catch (Throwable e) {
			logger.error(e.getMessage(), e);
		} finally {
			contentStream.close();
		}
		return file;
	}

	private Map<String, String> parseJson(String jsonString) {
		JsonReader jr = Json.createReader(new StringReader(jsonString));
		JsonObject jo = jr.readObject();
		jr.close();
		Map<String,String> jsonMap = new HashMap<String,String>();
		for(String key : jo.keySet()) {
			jsonMap.put(key, jo.getString(key));
		}
		return jsonMap;
	}
	
	private Response getResponse(String status, String message) {
		JsonObjectBuilder json = Json.createObjectBuilder();
		json.add("status", status);
		json.add("message", message != null ? message : "null");

		ResponseBuilder rb = null;
		if("error".equals(status))
			rb = Response.status(Response.Status.BAD_REQUEST);
		else
			rb = Response.status(Response.Status.OK);
		rb.entity(json.build());
		return rb.build();
	}

}
